package Controller;

import Model.InitialData;
import Simplex.Simplex;
import View.Application;

public class CohonController {

    private InitialData initialData = Application.initialData;
    private Simplex simplex;

    public double[][] execute(int r) {
        double resultFinal[][] = new double[r * initialData.getNumberObjetives()][initialData.getNumberVariables() + 2];
        double resultSimplex[][];
        double optimalS[][];
        double zmin[];
        double zmax[];
        double[] EkVector = new double[initialData.getNumberObjetives() * r];
        resultSimplex = applySimplex(initialData.getObjectiveData(), initialData.getRestrictionData(), initialData.getLdData());
        optimalS = calculateOptimalSolutions(resultSimplex, initialData.getObjectiveData());
        zmin = findMin(optimalS);
        zmax = findMax(optimalS);
        EkVector = defineE(r, zmax, zmin);
        resultFinal = solutionsNoDominated(EkVector, initialData.getObjectiveData(), r);
        return resultFinal;

    }

    /*Paso1: Se maximiza cada uno de los objetivos en forma individual sujeto al espacio de soluciones posibles.*/
    /**
     * Se aplica el método Simplex a cada una de las filas de la matriz de
     * objetivos.
     *
     * @param objectives
     * @param restrictionsA
     * @param restrictionsB
     */
    public double[][] applySimplex(double objectives[][], double restrictionsA[][], double restrictionsB[]) {
        //objectives = equivalence(objectives);
        double resultPrimal[];
        double results[][] = new double[initialData.getNumberVariables()][initialData.getNumberVariables()];
        double c[] = new double[initialData.getNumberVariables()];
        for (int i = 0; i < initialData.getNumberVariables(); i++) {
            simplex = new Simplex(restrictionsA, restrictionsB, objectives[i]);
            resultPrimal = simplex.primal();
            System.arraycopy(resultPrimal, 0, results[i], 0, initialData.getNumberVariables());

        }
        return results;

    }

    /*Paso 2: Se organizan las soluciones encontradas al optimizar cada objetivo en una matriz de pagos.*/
    /**
     * Toma la matriz de objetivos y la matriz de resultados del Simplex para
     * calcular las soluciones óptimas.
     *
     * @param results
     * @param objectives
     * @return optimalSolutions
     */
    public double[][] calculateOptimalSolutions(double results[][], double objectives[][]) {
        double optimalSolutions[][] = new double[initialData.getNumberObjetives()][initialData.getNumberObjetives()];
        for (int i = 0; i < initialData.getNumberObjetives(); i++) {
            for (int j = 0; j < initialData.getNumberObjetives(); j++) {
                optimalSolutions[i][j] = evaluate(results[i], objectives[j]);
            }
        }
        return optimalSolutions;
    }

    /**
     * Método que busca el valor máximo de cada columna de la matriz de pagos.
     *
     * @param optimalSolutions
     * @return
     */
    public double[] findMax(double optimalSolutions[][]) {
        double ZMax[] = new double[initialData.getNumberObjetives()];
        for (int i = 0; i < initialData.getNumberObjetives(); i++) {
            double max = optimalSolutions[0][i];
            for (int j = 0; j < initialData.getNumberObjetives(); j++) {
                if (optimalSolutions[j][i] > max) {
                    max = optimalSolutions[j][i];
                }
            }
            ZMax[i] = max;
        }
        return ZMax;
    }

    /**
     * Método que busca el valor mínimo de cada columna de la matriz de pagos.
     *
     * @param optimalSolutions
     * @return
     */
    public double[] findMin(double optimalSolutions[][]) {
        double ZMin[] = new double[initialData.getNumberObjetives()];
        for (int i = 0; i < initialData.getNumberObjetives(); i++) {
            double min = optimalSolutions[0][i];
            for (int j = 0; j < initialData.getNumberObjetives(); j++) {
                if (optimalSolutions[j][i] < min) {
                    min = optimalSolutions[j][i];
                }
            }
            ZMin[i] = min;
        }
        return ZMin;
    }
    /*Paso 4: Para cada objetivo se definen los r valores de Ek.*/

    public double[] defineE(int rValues, double ZMax[], double ZMin[]) {
        double E[] = new double[initialData.getNumberObjetives() * rValues];
        for (int i = 0; i < initialData.getNumberObjetives(); i++) {
            for (int j = 0; j < rValues; j++) {
                E[i * rValues + j] = ZMin[i] + ((j / (((double) rValues) - 1)) * (ZMax[i] - ZMin[i]));
                //System.out.print(" " + E[i * rValues + j]);
            }
        }
        return E;
    }
    /*
     * Evalula la funcion objetivo ;
     */

    public double evaluate(double[] function, double[] values) {
        double result = 0.0;
        for (int i = 0; i < function.length; i++) {
            result += function[i] * values[i];
        }
        return result;
    }

    public double[][] solutionsNoDominated(double ekVector[], double[][] objectives, int r) {

        double solutionsVectors[][] = new double[initialData.getNumberObjetives() * r][initialData.getNumberVariables()];
        double znx[] = new double[initialData.getNumberObjetives() * r];
        double restrictionsZ[][];
        double ldZ[];
        for (int i = 0; i < initialData.getNumberObjetives() * r; i++) {
            znx[i] = 0;
        }
        for (int i = 0; i < initialData.getNumberObjetives(); i++) {
            for (int j = 0; j < initialData.getNumberObjetives(); j++) {
                if (i != j) {
                    restrictionsZ = addZj(objectives[j]);
                    for (int k = i * r; k < i * r + r; k++) {
                        ldZ = addEj(ekVector[k]);
                        double objectivesZ[] = new double[objectives.length + 1];
                        System.arraycopy(objectives[i], 0, objectivesZ, 0, objectives[i].length);
                        objectivesZ[objectives[i].length] = 0.0;
                        Simplex s = new Simplex(restrictionsZ, ldZ, objectives[j]);
//                        double prim []= s.primal();
//                        for (int m=0;m<initialData.getNumberRestrictions()+1;m++){
//                            System.out.print("\n");
//                            for (int n=0;n<initialData.getNumberVariables()+1;n++){
//                                System.out.print(" "+restrictionsZ[m][n]);
//                            }                       
//                            System.out.print(" "+ldZ[m]+" "+objectivesZ[m]);
//                        }
                        znx[i * r + j] = evaluate(objectives[i], s.primal());
                        //System.out.print("\n " + znx[i * r + j]);
                        solutionsVectors[i * r + j] = s.primal();
                    }
                }
            }
        }
        double solutionsNoDominated[][] = new double[initialData.getNumberObjetives() * r][initialData.getNumberVariables() + 2];
        for (int i = 0; i < initialData.getNumberObjetives() * r; i++) {
            for (int j = 1; j < initialData.getNumberVariables() + 1; j++) {
                solutionsNoDominated[i][j] = solutionsVectors[i][j - 1];
            }
            solutionsNoDominated[i][0] = ekVector[i];
            solutionsNoDominated[i][initialData.getNumberVariables() + 1] = znx[i];
        }
//        for (int i = 0; i < initialData.getNumberObjetives() * r; i++) {
//            //System.out.print("\n");
//            for (int j = 0; j < initialData.getNumberVariables() + 2; j++) {
//                System.out.print(" "+solutionsNoDominated[i][j]);
//            }
        //}
        return solutionsNoDominated;

    }

    private double[][] addZj(double[] objective) {
        double r[][] = initialData.getRestrictionData();
        double rz[][] = new double[initialData.getNumberRestrictions() + 1][initialData.getNumberVariables() ];
        for (int i = 0; i < initialData.getNumberRestrictions(); i++) {
            for (int j = 0; j < initialData.getNumberVariables(); j++) {
                rz[i][j] = r[i][j];
            }
        }
//        for (int i = 0; i < initialData.getNumberRestrictions(); i++) {
//            rz[i][initialData.getNumberVariables()] = 0;
//        }
        for (int i = 0; i < objective.length; i++) {
            rz[initialData.getNumberRestrictions()][i] = objective[i] * -1;
        }
//        rz[initialData.getNumberRestrictions()][initialData.getNumberVariables()] = 1;
//        for (int i = 0; i < initialData.getNumberRestrictions() + 1; i++) {
//            System.out.print("\n ");
//            for (int j = 0; j < initialData.getNumberVariables() + 1; j++) {
//                System.out.print(" " + rz[i][j]);
//            }
//        }


        return rz;

    }

    private double[] addEj(double d) {
        double r[] = initialData.getLdData();
        double rz[] = new double[initialData.getLdData().length + 1];
        //System.out.print("\n"+r.length) ;
        for (int i = 0; i < initialData.getLdData().length; i++) {
            rz[i] = r[i];
        }
        rz[initialData.getLdData().length] = -1.0 * d;
        //System.out.print("\n") ;
        return rz;
    }
}
